package kz.pompei.ifacer.grot;

import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import kz.pompei.ifacer.grot.acts.Act;
import kz.pompei.ifacer.grot.acts.ActGroup;
import kz.pompei.ifacer.model.widgets.Widget;
import kz.pompei.ifacer.visio.WidgetProp;

public class Align {
  
  private final GrotPanel grot;
  
  public Align(GrotPanel grot) {
    this.grot = grot;
  }
  
  public Act toLeftSideSelected() throws Exception {
    return toLeftSide(grot.selected());
  }
  
  public Act toLeftSide(List<Widget> widgets) throws Exception {
    if (widgets.size() < 2) return null;
    
    Collections.sort(widgets, new Comparator<Widget>() {
      @Override
      public int compare(Widget o1, Widget o2) {
        return o1.y - o2.y;
      }
    });
    
    Widget first = widgets.remove(0);
    ActGroup ret = new ActGroup();
    Graphics2D g = grot.newG();
    Rectangle firstBound = grot.visio.getWidgetBound(g, first);
    for (Widget widget : widgets) {
      Act act = grot.visio.setProperty(g, widget, WidgetProp.LEFT, firstBound.x);
      if (act != null) ret.group.add(act);
    }
    g.dispose();
    return ret;
  }
  
  public Act toRightSideSelected() throws Exception {
    return toRightSide(grot.selected());
  }
  
  public Act toRightSide(List<Widget> widgets) throws Exception {
    if (widgets.size() < 2) return null;
    
    Collections.sort(widgets, new Comparator<Widget>() {
      @Override
      public int compare(Widget o1, Widget o2) {
        return o1.y - o2.y;
      }
    });
    
    Widget first = widgets.remove(0);
    ActGroup ret = new ActGroup();
    Graphics2D g = grot.newG();
    Rectangle firstBound = grot.visio.getWidgetBound(g, first);
    for (Widget widget : widgets) {
      Act act = grot.visio
          .setProperty(g, widget, WidgetProp.RIGHT, firstBound.x + firstBound.width);
      if (act != null) ret.group.add(act);
    }
    g.dispose();
    return ret;
  }
  
  public Act toTopSideSelected() throws Exception {
    return toTopSide(grot.selected());
  }
  
  public Act toTopSide(List<Widget> widgets) throws Exception {
    if (widgets.size() < 2) return null;
    
    Collections.sort(widgets, new Comparator<Widget>() {
      @Override
      public int compare(Widget o1, Widget o2) {
        return o1.x - o2.x;
      }
    });
    
    Widget first = widgets.remove(0);
    ActGroup ret = new ActGroup();
    Graphics2D g = grot.newG();
    Rectangle firstBound = grot.visio.getWidgetBound(g, first);
    for (Widget widget : widgets) {
      Act act = grot.visio.setProperty(g, widget, WidgetProp.TOP, firstBound.y);
      if (act != null) ret.group.add(act);
    }
    g.dispose();
    return ret;
  }
  
  public Act toBottomSideSelected() throws Exception {
    return toBottomSide(grot.selected());
  }
  
  public Act toBottomSide(List<Widget> widgets) throws Exception {
    if (widgets.size() < 2) return null;
    
    Collections.sort(widgets, new Comparator<Widget>() {
      @Override
      public int compare(Widget o1, Widget o2) {
        return o1.x - o2.x;
      }
    });
    
    Widget first = widgets.remove(0);
    ActGroup ret = new ActGroup();
    Graphics2D g = grot.newG();
    Rectangle firstBound = grot.visio.getWidgetBound(g, first);
    for (Widget widget : widgets) {
      Act act = grot.visio.setProperty(g, widget, WidgetProp.BOTTOM, firstBound.y
          + firstBound.height);
      if (act != null) ret.group.add(act);
    }
    g.dispose();
    return ret;
  }
  
  public Act toEqualWidthsSelected() throws Exception {
    return toEqualWidths(grot.selected());
  }
  
  public Act toEqualWidths(List<Widget> widgets) throws Exception {
    if (widgets.size() < 2) return null;
    
    Collections.sort(widgets, new Comparator<Widget>() {
      @Override
      public int compare(Widget o1, Widget o2) {
        int c = o1.x - o2.x;
        return c == 0 ? o1.y - o2.y :c;
      }
    });
    
    Widget first = widgets.remove(0);
    ActGroup ret = new ActGroup();
    Graphics2D g = grot.newG();
    Rectangle firstBound = grot.visio.getWidgetBound(g, first);
    for (Widget widget : widgets) {
      Act act = grot.visio.setProperty(g, widget, WidgetProp.WIDTH, firstBound.width);
      if (act != null) ret.group.add(act);
    }
    g.dispose();
    return ret;
  }
  
  public Act toEqualHeightsSelected() throws Exception {
    return toEqualHeights(grot.selected());
  }
  
  public Act toEqualHeights(List<Widget> widgets) throws Exception {
    if (widgets.size() < 2) return null;
    
    Collections.sort(widgets, new Comparator<Widget>() {
      @Override
      public int compare(Widget o1, Widget o2) {
        int c = o1.y - o2.y;
        return c == 0 ? o1.x - o2.x :c;
      }
    });
    
    Widget first = widgets.remove(0);
    ActGroup ret = new ActGroup();
    Graphics2D g = grot.newG();
    Rectangle firstBound = grot.visio.getWidgetBound(g, first);
    for (Widget widget : widgets) {
      Act act = grot.visio.setProperty(g, widget, WidgetProp.HEIGHT, firstBound.height);
      if (act != null) ret.group.add(act);
    }
    g.dispose();
    return ret;
  }
  
  public Act toOneVerticalCenterSelected() throws Exception {
    return toOneVerticalCenter(grot.selected());
  }
  
  public Act toOneVerticalCenter(List<Widget> widgets) throws Exception {
    if (widgets.size() < 2) return null;
    
    Collections.sort(widgets, new Comparator<Widget>() {
      @Override
      public int compare(Widget o1, Widget o2) {
        return o1.y - o2.y;
      }
    });
    
    Widget first = widgets.remove(0);
    ActGroup ret = new ActGroup();
    Graphics2D g = grot.newG();
    Rectangle firstBound = grot.visio.getWidgetBound(g, first);
    int xCenter = firstBound.x + firstBound.width / 2;
    for (Widget widget : widgets) {
      Rectangle r = grot.visio.getWidgetBound(g, widget);
      Act act = grot.visio.setProperty(g, widget, WidgetProp.LEFT, xCenter - r.width / 2);
      if (act != null) ret.group.add(act);
    }
    g.dispose();
    return ret;
  }
  
  public Act toOneBetweensVertSelected() throws Exception {
    return toOneBetweensVert(grot.selected());
  }
  
  public Act toOneBetweensVert(List<Widget> widgets) throws Exception {
    if (widgets.size() < 3) return null;
    
    Collections.sort(widgets, new Comparator<Widget>() {
      @Override
      public int compare(Widget o1, Widget o2) {
        return o1.y - o2.y;
      }
    });
    
    Widget first = widgets.remove(0);
    Widget second = widgets.remove(0);
    
    Graphics2D g = grot.newG();
    Rectangle firstBound = grot.visio.getWidgetBound(g, first);
    Rectangle cur = grot.visio.getWidgetBound(g, second);
    int dist = cur.y - (firstBound.y + firstBound.height);
    ActGroup ret = new ActGroup();
    for (Widget widget : widgets) {
      Rectangle r = grot.visio.getWidgetBound(g, widget);
      r.y = cur.y + cur.height + dist;
      Act act = grot.visio.setProperty(g, widget, WidgetProp.TOP, r.y);
      if (act != null) {
        ret.group.add(act);
        cur = r;
      }
    }
    g.dispose();
    return ret;
  }
  
  public Act toOneBetweensHorizSelected() throws Exception {
    return toOneBetweensHoriz(grot.selected());
  }
  
  public Act toOneBetweensHoriz(List<Widget> widgets) throws Exception {
    if (widgets.size() < 3) return null;
    
    Collections.sort(widgets, new Comparator<Widget>() {
      @Override
      public int compare(Widget o1, Widget o2) {
        return o1.x - o2.x;
      }
    });
    
    Widget first = widgets.remove(0);
    Widget second = widgets.remove(0);
    
    Graphics2D g = grot.newG();
    Rectangle firstBound = grot.visio.getWidgetBound(g, first);
    Rectangle cur = grot.visio.getWidgetBound(g, second);
    int dist = cur.x - (firstBound.x + firstBound.width);
    ActGroup ret = new ActGroup();
    for (Widget widget : widgets) {
      Rectangle r = grot.visio.getWidgetBound(g, widget);
      r.x = cur.x + cur.width + dist;
      Act act = grot.visio.setProperty(g, widget, WidgetProp.LEFT, r.x);
      if (act != null) {
        ret.group.add(act);
        cur = r;
      }
    }
    g.dispose();
    return ret;
  }
}
